/*  Copyright 2009 Google Inc.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.


  PopupManager is a library to facilitate integration with OpenID
  identity providers (OP)s that support a pop-up authentication interface.
  To create a popup window, you first construct a popupOpener customized
  for your site and a particular identity provider, E.g.:

  var googleOpener = popupManager.createOpener(openidParams);

  where 'openidParams' are customized for Google in this instance.
  (typically you just change the openidpoint, the version number
  (the openid.ns parameter) and the extensions based on what
  the OP supports.
  OpenID libraries can often discover these properties
  automatically from the location of an XRD document.

  Then, you can either directly call
  googleOpener.popup(width, height), where 'width' and 'height' are your choices
  for popup size, or you can display a button 'Sign in with Google' and set the
..'onclick' handler of the button to googleOpener.popup() */

var popupManager = {};

// Library constants

popupManager.constants = {
    'darkCover' : 'popupManager_darkCover_div',
    'darkCoverStyle' : ['position:absolute;',
                      'top:0px;',
                      'left:0px;',
                      'padding-right:0px;',
                      'padding-bottom:0px;',
                      'background-color:#000000;',
                      'opacity:0.5;', //standard-compliant browsers
                      '-moz-opacity:0.5;',           // old Mozilla 
                      'filter:alpha(opacity=0.5);',  // IE
                      'z-index:10000;',
                      'width:100%;',
                      'height:100%;'
                      ].join(''),
    'openidSpec' : {
     'identifier_select' : 'http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select',
     'namespace2' : 'http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0'
    } 
};

/* Computes the size of the window contents. Returns a pair of
 coordinates [width, height] which can be [0, 0] if it was not possible
 to compute the values.*/
popupManager.getWindowInnerSize = function() 
{
    var width = 0;
    var height = 0;
    var elem = null;
    if ('innerWidth' in window) {
        // For non-IE
        width = window.innerWidth;
        height = window.innerHeight;
    } else {
        // For IE,
        if (('BackCompat' === window.document.compatMode)
        && ('body' in window.document)) {
        elem = window.document.body;
        } else if ('documentElement' in window.document) {
        elem = window.document.documentElement;
        }
        if (elem !== null) {
        width = elem.offsetWidth;
        height = elem.offsetHeight;
        }
    }
    return [width, height];
};

/* Computes the coordinates of the parent window.
 Gets the coordinates of the parent frame*/
popupManager.getParentCoords = function() {
    var width = 0;
    var height = 0;
    if ('screenLeft' in window) {
        // IE-compatible variants
        width = window.screenLeft;
        height = window.screenTop;
    } else if ('screenX' in window) {
        // Firefox-compatible
        width = window.screenX;
        height = window.screenY;
    }
    return [width, height];
};

/* Computes the coordinates of the new window, so as to center it
 over the parent frame*/
popupManager.getCenteredCoords = function(width, height) {
    var parentSize = this.getWindowInnerSize();
    var parentPos = this.getParentCoords();
    var xPos = parentPos[0] + Math.max(0, Math.floor((parentSize[0] - width) / 2));
    var yPos = parentPos[1] + Math.max(0, Math.floor((parentSize[1] - height) / 2));
    return [xPos, yPos];
};

/*  A utility class, implements an onOpenHandler that darkens the screen
  by overlaying it with a semi-transparent black layer. To use, ensure that
  no screen element has a z-index at or above 10000.
  This layer will be suppressed automatically after the screen closes.

  Note: If you want to perform other operations before opening the popup, but
  also would like the screen to darken, you can define a custom handler
  as such:
  var myOnOpenHandler = function(inputs) {
    .. do something
    popupManager.darkenScreen();
    .. something else
  };
  Then you pass myOnOpenHandler as input to the opener, as in:
  var openidParams = {};
  openidParams.onOpenHandler = myOnOpenHandler;
  ... other customizations
  var myOpener = popupManager.createOpener(openidParams);*/
popupManager.darkenScreen = function() {
    var darkCover = window.document.getElementById(window.popupManager.constants['darkCover']);
    if (!darkCover) {
        darkCover = window.document.createElement('div');
        darkCover['id'] = window.popupManager.constants['darkCover'];
        darkCover.setAttribute('style', window.popupManager.constants['darkCoverStyle']);
        window.document.body.appendChild(darkCover);
    }
    darkCover.style.visibility = 'visible';
};

/*  Returns a an object that can open a popup window customized for an OP & RP.
  to use you call var opener = popupManager.cretePopupOpener(openidParams);
  and then you can assign the 'onclick' handler of a button to
  opener.popup(width, height), where width and height are the values of the popup size;

  To use it, you would typically have code such as:
  var myLoginCheckFunction = ...  some AJAXy call or page refresh operation
  that will cause the user to see the logged-in experience in the current page.
  var openidParams = { realm : 'openid.realm', returnToUrl : 'openid.return_to',
  opEndpoint : 'openid.op_endpoint', onCloseHandler : myLoginCheckFunction,
  shouldEncodeUrls : 'true' (default) or 'false', extensions : myOpenIDExtensions };

  Here extensions include any OpenID extensions that you support. For instance,
  if you support Attribute Exchange v.1.0, you can say:
  (Example for attribute exchange request for email and name,
  assuming that shouldEncodeUrls = 'true':)
  var myOpenIDExtensions = {
      'openid.ax.ns' : 'http://openid.net/srv/ax/1.0',
      'openid.ax.type.email' : 'http://axschema.org/contact/email',
      'openid.ax.type.name1' : 'http://axschema.org/namePerson/first',
      'openid.ax.type.name2' : 'http://axschema.org/namePerson/last',
      'openid.ax.required' : 'email,name1,name2' };
  Note that the 'ui' namespace is reserved by this library for the OpenID
  UI extension, and that the mode 'popup' is automatically applied.
  If you wish to make use of the 'language' feature of the OpenID UI extension
  simply add the following entry (example assumes the language requested
  is Swiss French:
  var my OpenIDExtensions = {
    ... // other extension parameters
    'openid.ui.language' : 'fr_CH',
    ... };*/
popupManager.createPopupOpener = (function(openidParams) {                      
        var interval_ = null;
        var popupWindow_ = null;
        var that = this;
        var shouldEscape_ = ('shouldEncodeUrls' in openidParams) ? openidParams.shouldEncodeUrls : true;
        var encodeIfRequested_ = function(url) 
        {
            return (shouldEscape_ ? encodeURIComponent(url) : url);
        };
        var identifier_ = ('identifier' in openidParams) ? encodeIfRequested_(openidParams.identifier) : this.constants.openidSpec.identifier_select;
        var identity_ = ('identity' in openidParams) ? encodeIfRequested_(openidParams.identity) : this.constants.openidSpec.identifier_select;
        var openidNs_ = ('namespace' in openidParams) ? encodeIfRequested_(openidParams.namespace) : this.constants.openidSpec.namespace2;
        var onOpenHandler_ = (('onOpenHandler' in openidParams) && ('function' === typeof(openidParams.onOpenHandler))) ? openidParams.onOpenHandler : this.darkenScreen;
        var onCloseHandler_ = (('onCloseHandler' in openidParams) && ('function' === typeof(openidParams.onCloseHandler))) ? openidParams.onCloseHandler : null;
        var returnToUrl_ = ('returnToUrl' in openidParams) ? openidParams.returnToUrl : null;
        var realm_ = ('realm' in openidParams) ? openidParams.realm : null;
        var endpoint_ = ('opEndpoint' in openidParams) ? openidParams.opEndpoint : null;
        var extensions_ = ('extensions' in openidParams) ? openidParams.extensions : null;

        // processes key value pairs, escaping any input;
        var keyValueConcat_ = function(keyValuePairs) {
        var result = "";
        for (key in keyValuePairs) 
        {
            result += ['&', key, '=', encodeIfRequested_(keyValuePairs[key])].join('');
        }
        return result;
    };

    //Assembles the OpenID request from customizable parameters
    var buildUrlToOpen_ = function() 
    {
        var connector = '&';
        var encodedUrl = null;
        var urlToOpen = null;
        if ((null === endpoint_) || (null === returnToUrl_)) 
        {
            return;
        }
        if (endpoint_.indexOf('?') === -1) 
        {
            connector = '?';
        }
        encodedUrl = encodeIfRequested_(returnToUrl_);
        urlToOpen = [ endpoint_, connector,
        'openid.ns=', openidNs_,
        '&openid.mode=checkid_setup',
        '&openid.claimed_id=', identifier_,
        '&openid.identity=', identity_,
        '&openid.return_to=', encodedUrl ].join('');
        if (realm_ !== null) 
        {
            urlToOpen += "&openid.realm=" + encodeIfRequested_(realm_);
        }
        if (extensions_ !== null) 
        {
            urlToOpen += keyValueConcat_(extensions_);
        }
        urlToOpen += '&openid.ns.ui=' + encodeURIComponent('http://specs.openid.net/extensions/ui/1.0');
        urlToOpen += '&openid.ui.mode=popup';
        return urlToOpen;
    };

    // Tests that the popup window has closed
    var isPopupClosed_ = function() 
    {          
        return (!popupWindow_ || popupWindow_.closed); 
    };

    // Check to perform at each execution of the timed loop. It also triggers
    // the action that follows the closing of the popup
    var waitForPopupClose_ = function() 
    {        
        if (isPopupClosed_()) 
        {                 
            popupWindow_ = null;
            var darkCover = window.document.getElementById(window.popupManager.constants['darkCover']);
            if (darkCover) 
            {
                darkCover.style.visibility = 'hidden';
            }        //alert(googleClose);
            if (onCloseHandler_ !== null) 
            {
                onCloseHandler_();
            }  
            if ((null !== interval_)) 
            {
                window.clearInterval(interval_);
                interval_ = null;
            }   
        }
    };

    return {
        // Function that opens the window.
        popup: function(width, height) 
        {              
            var urlToOpen = buildUrlToOpen_();
            if (onOpenHandler_ !== null) 
            {
                onOpenHandler_();
            }
            var coordinates = that.getCenteredCoords(width, height);
            popupWindow_ = window.open(urlToOpen, "",
                "width=" + width + ",height=" + height +
                ",status=1,location=1,resizable=yes" +
                ",left=" + coordinates[0] +",top=" + coordinates[1]
            );
            interval_ = window.setInterval(waitForPopupClose_, 80);
            return true;
        }
    };
});